package cn.spdb.harrier.common.dynamicjava;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;

public class DynamicClassLoader extends URLClassLoader {

	public DynamicClassLoader(ClassLoader parent) {
		super(new URL[0], parent);
	}
	
	public DynamicClassLoader(URL[] urls,ClassLoader parent) {
		super(urls, parent);
	}

/**
 * 根据指定的类名和 Java 字节码对象加载 Class。
 *
 * <p>该方法用于将编译后的字节码（byte[]）通过类加载器定义为一个 Class 对象，
 * 并绑定到指定的类名。
 *
 * @param fullName         类的全限定名（如：com.example.MyClass）
 * @param javaClassObject  包含字节码的 JavaClassObject 对象
 * @return 加载成功的 Class 对象，失败返回 null
 */
public Class<?> loadClass(String fullName, JavaClassObject javaClassObject) {
    // 获取字节码数据
    byte[] classData = javaClassObject.getBytes();

    // 使用 defineClass 将字节码转换为 Class 对象，并绑定类名
    Class<?> clazz = this.defineClass(fullName, classData, 0, classData.length);

    return clazz;
}

/**
 * 直接根据 Java 字节码加载 Class，不指定类名。
 *
 * <p>该方法适用于类名由字节码自身决定的情况，
 * 通常用于动态编译生成的类，由 JVM 自动解析类名。
 *
 * @param javaClassObject 包含字节码的 JavaClassObject 对象
 * @return 加载成功的 Class 对象，失败返回 null
 */
public Class<?> loadClass(JavaClassObject javaClassObject) {
    // 获取字节码数据
    byte[] classData = javaClassObject.getBytes();

    // 不指定类名，由 JVM 自动解析并定义类
    Class<?> clazz = this.defineClass(null, classData, 0, classData.length);

    return clazz;
}

/**
 * 从本地 `.class` 文件中加载 Class。
 *
 * <p>该方法读取磁盘上的 `.class` 文件内容为字节数组，
 * 然后调用 defineClass 方法将其定义为 Class 对象。
 *
 * @param file .class 文件对象
 * @return 加载成功的 Class 对象，失败抛出 IOException
 * @throws IOException 如果文件读取失败或不是一个合法的类文件
 */
public Class<?> loadClass(File file) throws IOException {
    // 创建与文件大小一致的字节数组
    byte[] bytes = new byte[(int) file.length()];
    Class<?> clazz = null;

    // 使用 try-with-resources 自动关闭流
    try (FileInputStream inputStream = new FileInputStream(file)) {
        int j = 0;
        while (true) {
            // 分段读取字节码
            int i = inputStream.read(bytes);
            if (i == -1) {
                break; // 读取完成退出循环
            }
            j += i;
        }

        // 使用父类的 defineClass 方法定义类
        clazz = super.defineClass(null, bytes, 0, j);
    } catch (IOException e) {
        e.printStackTrace();
        throw e;
    }

    return clazz;
}

}
